/*
* Author: Chris Seguin
*
* This software has been developed under the copyleft
* rules of the GNU General Public License. Please
* consult the GNU General Public License for more
* details about use and distribution of this software.
*/
package org.acm.seguin.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import org.acm.seguin.awt.ExceptionPrinter;
import org.acm.seguin.tools.install.RefactoryInstaller;
/**
* Settings loaded from a file
*
*@author Chris Seguin
*@created October 3, 1999
*/
public class FileSettings implements Settings {
private String app;
private String type;
private File file;
private long lastModified;
private Properties props;
private boolean continuallyReload;
private boolean reloadNow;
private FileSettings parent;
private static Hashtable map = null;
private static File settingsRoot = null;
/**
* Constructor for the FileSettings object
*
*@param express The file to use for loading
*@exception MissingSettingsException The file is not found
*/
public FileSettings(File express) throws MissingSettingsException {
file = express;
if (!file.exists()) {
throw new NoSettingsFileException(app, type);
}
load();
this.app = express.getParent();
this.type = express.getName();
continuallyReload = false;
reloadNow = false;
parent = null;
}
/**
* Constructor for the FileSettings object
*
*@param app The application name
*@param type The application type
*@exception MissingSettingsException The file is not found
*/
protected FileSettings(String app, String type) throws MissingSettingsException {
File directory = new File(getSettingsRoot(), "." + app);
if (!directory.exists()) {
directory.mkdirs();
throw new NoSettingsFileException(app, type);
}
file = new File(directory, type + ".settings");
if (!file.exists()) {
throw new NoSettingsFileException(app, type);
}
load();
this.app = app;
this.type = type;
continuallyReload = false;
reloadNow = false;
parent = null;
}
/**
* Sets the ContinuallyReload attribute of the FileSettings object
*
*@param way The new ContinuallyReload value
*/
public void setContinuallyReload(boolean way) {
continuallyReload = way;
}
/**
* Sets the ReloadNow attribute of the FileSettings object
*
*@param way The new ReloadNow value
*/
public void setReloadNow(boolean way) {
reloadNow = way;
if (reloadNow) {
load();
}
}
/**
* Gets the keys associated with this properties
*
*@return the iterator
*/
public Enumeration getKeys() {
if (!isUpToDate()) {
load();
}
reloadNow = false;
return props.keys();
}
/**
* Gets a string
*
*@param code The code to look up
*@return The associated string
*/
public String getString(String code) {
if (!isUpToDate()) {
load();
}
reloadNow = false;
String result = props.getProperty(code);
if ((result == null) && (parent != null)) {
result = parent.getString(code);
}
if (result == null) {
throw new SettingNotFoundException(app, type, code);
}
return result;
}
/**
* Gets a integer
*
*@param code The code to look up
*@return The associated integer
*/
public int getInteger(String code) {
try {
return Integer.parseInt(getString(code));
}
catch (NumberFormatException mfe) {
throw new SettingNotFoundException(app, type, code);
}
}
/**
* Gets a double
*
*@param code The code to look up
*@return The associated double
*/
public double getDouble(String code) {
try {
Double value = new Double(getString(code));
return value.doubleValue();
}
catch (NumberFormatException mfe) {
throw new SettingNotFoundException(app, type, code);
}
}
/**
* Gets a boolean
*
*@param code The code to look up
*@return The associated boolean
*/
public boolean getBoolean(String code) {
try {
Boolean value = new Boolean(getString(code));
return value.booleanValue();
}
catch (NumberFormatException mfe) {
throw new SettingNotFoundException(app, type, code);
}
}
/**
* Sets the Parent attribute of the FileSettings object
*
*@param value The new Parent value
*/
protected void setParent(FileSettings value) {
parent = value;
}
/**
* Get the escaped character
*
*@param ch the character
*@return The character it should be replaced with
*/
private char getSpecial(char ch) {
switch (ch) {
case 'b':
return (char) 8;
case 'r':
return (char) 13;
case 'n':
return (char) 10;
case 'f':
return (char) 12;
case 't':
return (char) 9;
default:
return ch;
}
}
/**
* Returns true if the file is up to date. This method is used to determine
* if it is necessary to reload the file.
*
*@return true if it is up to date.
*/
private boolean isUpToDate() {
if (continuallyReload || reloadNow) {
return (lastModified == file.lastModified());
}
// Assume that it is up to date
return true;
}
/**
* Loads all the settings from the file
*/
private synchronized void load() {
//System.out.println("Loading from: " + file.getPath() + " " + file.length());
props = new Properties();
try {
BufferedReader input = new BufferedReader(new FileReader(file));
String line = input.readLine();
while (line != null) {
if ((line.length() == 0) || (line.charAt(0) == '#')) {
// Comment - skip the line
}
else {
int equalsAt = line.indexOf('=');
if (equalsAt > 0) {
String key = line.substring(0, equalsAt);
String value = unescapeChars(line.substring(equalsAt + 1));
props.put(key, value);
}
}
line = input.readLine();
}
input.close();
}
catch (IOException ioe) {
ExceptionPrinter.print(ioe);
}
setReloadNow(false);
lastModified = file.lastModified();
}
/**
* A transformation on the characters in the string
*
*@param value the string we are updating
*@return the updated string
*/
private String unescapeChars(String value) {
StringBuffer buffer = new StringBuffer();
int last = value.length();
for (int ndx = 0; ndx < last; ndx++) {
char ch = value.charAt(ndx);
if (ch == '\\') {
char nextChar = value.charAt(ndx + 1);
char result = ' ';
if (nextChar == 'u') {
result = unicode(value, ndx);
ndx += 5;
}
else if (Character.isDigit(nextChar)) {
result = octal(value, ndx);
ndx += 3;
}
else if (ndx == last - 1) {
// Continuation...
}
else {
result = getSpecial(nextChar);
ndx++;
}
buffer.append(result);
}
else {
buffer.append(ch);
}
}
return buffer.toString();
}
/**
* Determine the unicode character
*
*@param value Description of Parameter
*@param ndx Description of Parameter
*@return Description of the Returned Value
*/
private char unicode(String value, int ndx) {
String hex = value.substring(ndx + 2, ndx + 6);
int result = Integer.parseInt(hex, 16);
return (char) result;
}
/**
* Determine the octal character
*
*@param value Description of Parameter
*@param ndx Description of Parameter
*@return Description of the Returned Value
*/
private char octal(String value, int ndx) {
String oct = value.substring(ndx + 1, ndx + 4);
int result = Integer.parseInt(oct, 8);
return (char) result;
}
/**
* Sets the root directory for settings files
*
*@param dir The new SettingsRoot value
*/
public static void setSettingsRoot(String dir) {
settingsRoot = new File(dir);
}
/**
* Sets the root directory for settings files
*
*@param dir The new SettingsRoot value
*/
public static void setSettingsRoot(File dir) {
settingsRoot = dir;
}
/**
* Factory method to create FileSettings objects
*
*@param app The name of the application
*@param name The name of the specific settings
*@return A settings object
*/
public static FileSettings getSettings(String app, String name) {
if (map == null) {
init();
}
String key = app + "::" + name;
FileSettings result = (FileSettings) map.get(key);
if (result == null) {
result = new FileSettings(app, name);
map.put(key, result);
}
return result;
}
/**
* Gets the SettingsRoot attribute of the FileSettings class
*
*@return The SettingsRoot value
*/
public static String getSettingsRoot() {
if (settingsRoot == null) {
initRootDir();
}
return settingsRoot.getPath();
}
/**
* Main program to test the FileSettings object
*
*@param args the command line arguments
*/
public static void main(String[] args) {
// Make sure everything is installed properly
(new RefactoryInstaller(false)).run();
String key = "author";
if (args.length > 0) {
key = args[0];
}
String type = "pretty";
if (args.length > 1) {
type = args[1];
}
String app = "Refactory";
if (args.length > 2) {
app = args[2];
}
System.out.println("Found: " + (new FileSettings(app, type)).getString(key));
}
/**
* Initializes static variables
*/
private static synchronized void init() {
if (map == null) {
map = new Hashtable();
initRootDir();
}
}
/**
* Initializes the root directory
*/
private static void initRootDir() {
if (settingsRoot != null) {
return;
}
String javaHome = System.getProperty("jrefactory.home");
if (javaHome != null) {
//System.out.println("Home: " + javaHome);
settingsRoot = new File(javaHome);
return;
}
javaHome = System.getProperty("user.home");
if (javaHome != null) {
//System.out.println("Home: " + javaHome);
settingsRoot = new File(javaHome);
return;
}
settingsRoot = new File("~/");
if (settingsRoot.exists()) {
//System.out.println("Home: ~/");
return;
}
settingsRoot = new File("C:\\winnt\\profiles");
if (settingsRoot.exists()) {
File attempt = new File(settingsRoot, System.getProperty("user.name"));
if (attempt.exists()) {
//System.out.println("Home: C:\\winnt\\profiles\\currentuser");
settingsRoot = attempt;
return;
}
}
settingsRoot = new File("c:\\windows");
//System.out.println("Home: C:\\windows");
}
}